#include "client.h"

int fileContentHash(const char *filePath, char hash[])
{
    // 申请一个数据结构
    SHA_CTX ctx;
    // 初始化
    SHA1_Init(&ctx);
    int fd = open(filePath, O_RDWR);
    char buf[4096];
    while(1){
        bzero(buf,sizeof(buf));
        ssize_t sret = read(fd,buf,sizeof(buf));
        SHA1_Update(&ctx,buf,sret);//把读到的文件内容更新到ctx
        if(sret == 0){
            break;
        }
    }
    // Final
    unsigned char md[20]; //保存2进制的哈希值
    SHA1_Final(md,&ctx);
    char sha1str[41] = {0};//保存一个字符串，内容是16进制数
    for(int i = 0; i < 20; ++i){
        char temp[3] = {0}; // 8bit二进制 --> 2位16进制
        sprintf(temp,"%02x",md[i]);
        strcat(sha1str,temp); //拼接到结果中
    }
    strncpy(hash, sha1str, 40);
    hash[40] = '\0';
    return 0;
}


int gets_client(int sockfd, char *filename){
    tlvPacket_t packet;
    memset(&packet, 0, sizeof(packet));

    // 1.发送文件名
    packet.type = GETS;
    packet.length = strlen(filename);
    memcpy(packet.value, filename, packet.length);
    send(sockfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);

    // 2.接收文件是否存在的错误信息(不太确定后续需要单独测试)
    int error_flag;
    recv(sockfd, &error_flag, sizeof(error_flag), MSG_WAITALL);
    if(error_flag == 0){ // 服务器返回错误
        printf("%s\n", "filename error, please input again!");
        return -1;
    }

    // 打开/创建文件
    umask(0);
    int fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);

    // 接受文件大小
    off_t filesize;
    memset(&packet, 0, sizeof(packet));
    recv(sockfd, (char *)&packet.type, sizeof(packet.type), MSG_WAITALL);
    recv(sockfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
    recv(sockfd, packet.value, packet.length, MSG_WAITALL);
    filesize = *((int*)packet.value);

    // 若文件大于100M, 用mmap
    if(filesize > 104857600){
        ftruncate(fd, filesize);
        char *p = (char *)mmap(NULL, filesize, PROT_WRITE, MAP_SHARED, fd, 0);
        recv(sockfd, p, filesize, MSG_WAITALL);
        munmap(p, filesize);
    }else{
        while(1){
            memset(&packet, 0, sizeof(packet));
            recv(sockfd, (char *)&packet.type, sizeof(packet.type), MSG_WAITALL);
            recv(sockfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
            if(packet.length > 0){
                recv(sockfd, packet.value, packet.length, MSG_WAITALL);
                write(fd, packet.value, packet.length);
            }
            else{
                break;
            }

        }

    }
    close(fd);
    //printf("recv complete\n");
    return 0;
}

int puts_client(int sockfd, char *filename){
    tlvPacket_t packet;
    int ifExist = 1; // 文件是否存在

    //判断文件名错误的处理
    if(access(filename, F_OK) != 0){
        printf("filename error, please input again!\n");
        ifExist = -1;
        send(sockfd, &ifExist, sizeof(ifExist), MSG_NOSIGNAL);
        return -1;
    }
    send(sockfd, &ifExist, sizeof(ifExist), MSG_NOSIGNAL);

    // 计算文件哈希值
    char hash[41] = {0};
    fileContentHash(filename, hash);
    // printf("hash = %s\n", hash);
    // printf("strhash = %ld\n", strlen(hash));

    // 将文件哈希发送给服务端
    //send(sockfd, hash, sizeof(hash), MSG_NOSIGNAL);
    // 用tlv发送
    memset(&packet, 0, sizeof(packet));
    packet.type = PUTS;
    packet.length = strlen(hash);
    memcpy(packet.value, hash, packet.length);
    send(sockfd, &packet, sizeof(packet.type)+sizeof(packet.length)+packet.length, MSG_NOSIGNAL);

    memset(&packet, 0, sizeof(packet));
    // 发送文件名
    packet.type = PUTS;
    packet.length = strlen(filename);
    memcpy(packet.value, filename, packet.length);
    send(sockfd, &packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);

    // 是否秒传？
    int transFlag;
    recv(sockfd, &transFlag, sizeof(transFlag), MSG_WAITALL);

    if(transFlag == 0) {
        // 可读打开上传文件
        int fd = open(filename, O_RDONLY);

        // 获取上传文件大小
        struct stat statbuf;
        fstat(fd, &statbuf);
        off_t filesize = statbuf.st_size;
        //printf("filesize = %lld\n", (long long)filesize);

        // 发送文件大小
        memset(&packet, 0, sizeof(packet));
        packet.type = PUTS;
        packet.length = sizeof(filesize);
        memcpy(packet.value, &filesize, packet.length);
        //memmove(packet.value, &filesize, packet.length);
        send(sockfd, (char *)&packet, sizeof(packet.type)+sizeof(packet.length) + packet.length, MSG_NOSIGNAL);

        // 若传输文件大于100M, 采用mmap
        if(filesize > 104857600){
            char *p = (char *)mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);
            send(sockfd, p, filesize, MSG_NOSIGNAL);
            munmap(p, filesize);
        }else{
            while(1){
                memset(&packet, 0, sizeof(packet));
                packet.type = PUTS;
                packet.length = read(fd, packet.value, sizeof(packet.value));
                if(packet.length > 0){
                    send(sockfd, (char *)&packet, sizeof(packet.type)+sizeof(packet.length) + packet.length, MSG_NOSIGNAL);
                }else{
                    break;
                }
            }

            // 发送结束标志
            memset(&packet, 0, sizeof(packet));
            packet.type = PUTS;
            send(sockfd, (char *)&packet,sizeof(packet.type)+sizeof(packet.length) + packet.length, MSG_NOSIGNAL);
        }
        close(fd);

    } else if (transFlag == 2){
        printf("File already exists.\n");
        return 0;
    }
    //printf("send complete\n");
    return 0;
}
